// AWTGridBagLayout.cpp: implementation of the AWTGridBagLayout class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
//#include "AWTTest.h"
#include "AWTGridBagLayout.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

IMPLEMENT_DYNAMIC(AWTGridBagLayout, AWTLayout)
IMPLEMENT_DYNAMIC(AWTGridBagLayoutInfo, AWTObject)

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

AWTGridBagLayout::AWTGridBagLayout()
{
	m_columnWidths = NULL;	
	m_rowHeights = NULL;	
	m_columnWeights = NULL;	
	m_rowWeights = NULL;
	m_layoutInfo = NULL;	
	m_columnWidthsLength = 0;
	m_rowHeightsLength = 0;
	m_columnWeightsLength = 0;
	m_rowWeightsLength = 0;
}

AWTGridBagLayout::~AWTGridBagLayout()
{
	if (m_layoutInfo != NULL)
		delete m_layoutInfo;

	POSITION pos;
	LPVOID p1, p2;
	AWTGridBagConstraints* c;
	for(pos = m_comptable.GetStartPosition(); pos != NULL;)    
	{
    m_comptable.GetNextAssoc(pos, p1, p2);
		c = (AWTGridBagConstraints*)p2;
		delete c;
	}
}

AWTGridBagConstraints* AWTGridBagLayout::getConstraints(AWTComponent* comp)
{
	void* pv = NULL;
	if (!(m_comptable.Lookup(comp, pv)))
	{ 
		setConstraints(comp, &m_defaultConstraints);
		m_comptable.Lookup(comp, pv);
	}
	return (AWTGridBagConstraints*)pv;
} 

AWTGridBagConstraints* AWTGridBagLayout::lookupConstraints(AWTComponent* comp)
{
	void* pv = NULL;
	if (!(m_comptable.Lookup(comp, pv)))
	{ 
		setConstraints(comp, &m_defaultConstraints);
		m_comptable.Lookup(comp, pv);
	}
	return (AWTGridBagConstraints*)pv;
} 

CPoint AWTGridBagLayout::getLayoutOrigin()
{
	CPoint origin(0,0); 
	if (m_layoutInfo != NULL) 
	{
		origin.x = m_layoutInfo->m_startx;
		origin.y = m_layoutInfo->m_starty;
  }
  return origin;
}

AWTGridBagLayoutInfo* AWTGridBagLayout::GetLayoutInfo(AWTContainer* parent, int sizeflag) 
{
    AWTGridBagLayoutInfo* r = new AWTGridBagLayoutInfo();
    AWTComponent* comp;
    AWTGridBagConstraints* constraints;
    CSize d;
		CObArray components;
    parent->getComponents(&components);

    int compindex=0, i=0, /*j,*/ k=0, px=0, py=0, pixels_diff=0, nextSize=0;
    int curX=0, curY=0, curWidth=0, curHeight=0, curRow=0, curCol=0;
    double weight_diff=0.0, weight=0.0/*, start, size*/;
    int* xMax=NULL, *yMax=NULL;

    /*
     * Pass #1
     *
     * Figure out the dimensions of the layout grid (use a value of 1 for
     * zero or negative widths and heights).
     */
    
    r->m_width = r->m_height = 0;
    curRow = curCol = -1;
    xMax = new int[MAXGRIDSIZE];
    yMax = new int[MAXGRIDSIZE];
		for (i = 0; i < MAXGRIDSIZE; i++)
			xMax[i] = yMax[i] = 0;

    for (compindex = 0 ; compindex < components.GetSize() ; compindex++) 
		{
      comp = (AWTComponent*)components[compindex];
      if (!comp->isVisible())
				continue;
      constraints = lookupConstraints(comp);
      
      curX = constraints->m_gridx;
      curY = constraints->m_gridy;
      curWidth = constraints->m_gridwidth;
      if (curWidth <= 0)
				curWidth = 1;
      curHeight = constraints->m_gridheight;
      if (curHeight <= 0)
				curHeight = 1;
      
      /* If x or y is negative, then use relative positioning: */
      if (curX < 0 && curY < 0) 
			{
				if (curRow >= 0)
					curY = curRow;
				else if (curCol >= 0)
					curX = curCol;
				else
					curY = 0;
      }
      if (curX < 0) 
			{
				px = 0;
				for (i = curY; i < (curY + curHeight); i++)
					px = max(px, xMax[i]);
	
				curX = px - curX - 1;
				if(curX < 0)
					curX = 0;
      }
      else if (curY < 0) 
			{
				py = 0;
				for (i = curX; i < (curX + curWidth); i++)
				py = max(py, yMax[i]);
	
				curY = py - curY - 1;
				if(curY < 0)
					curY = 0;
      }
      
      /* Adjust the grid width and height */
      for (px = curX + curWidth; r->m_width < px; r->m_width++);
      for (py = curY + curHeight; r->m_height < py; r->m_height++);
      
      /* Adjust the xMax and yMax arrays */
      for (i = curX; i < (curX + curWidth); i++) { yMax[i] = py; }
      for (i = curY; i < (curY + curHeight); i++) { xMax[i] = px; }
      
      /* Cache the current slave's size. */
      if (sizeflag == PREFERREDSIZE)
				d = comp->getPreferredSize();
      else
				d = comp->getMinimumSize();
      constraints->m_minWidth = d.cx;
      constraints->m_minHeight = d.cy;
      
      /* Zero width and height must mean that this is the last item (or
       * else something is wrong). */
      if (constraints->m_gridheight == 0 && constraints->m_gridwidth == 0)
				curRow = curCol = -1;
      
      /* Zero width starts a new row */
      if (constraints->m_gridheight == 0 && curRow < 0)
				curCol = curX + curWidth;
      
      /* Zero height starts a new column */
      else if (constraints->m_gridwidth == 0 && curCol < 0)
				curRow = curY + curHeight;
    }
    
    /*
     * Apply minimum row/column dimensions
     */
    if (m_columnWidths != NULL && r->m_width < m_columnWidthsLength)
      r->m_width = m_columnWidthsLength;
    if (m_rowHeights != NULL && r->m_height < m_rowHeightsLength)
      r->m_height = m_rowHeightsLength;

    /*
     * Pass #2
     *
     * Negative values for gridX are filled in with the current x value.
     * Negative values for gridY are filled in with the current y value.
     * Negative or zero values for gridWidth and gridHeight end the current
     *  row or column, respectively.
     */
    
    curRow = curCol = -1;
		delete xMax;
		delete yMax;
    xMax = new int[MAXGRIDSIZE];
    yMax = new int[MAXGRIDSIZE];
		for (i = 0; i < MAXGRIDSIZE; i++)
			yMax[i] = xMax[i] = 0;

    
    for (compindex = 0; compindex < components.GetSize() ; compindex++) 
		{
      comp = (AWTComponent*)components[compindex];
      if (!comp->isVisible())
				continue;
      constraints = lookupConstraints(comp);

      curX = constraints->m_gridx;
      curY = constraints->m_gridy;
      curWidth = constraints->m_gridwidth;
      curHeight = constraints->m_gridheight;

      /* If x or y is negative, then use relative positioning: */
      if (curX < 0 && curY < 0) 
			{
				if(curRow >= 0)
					curY = curRow;
				else if(curCol >= 0)
					curX = curCol;
				else
					curY = 0;
      }

      if (curX < 0) 
			{
				if (curHeight <= 0) 
				{
					curHeight += r->m_height - curY;
					if (curHeight < 1)
						curHeight = 1;
				}

				px = 0;
				for (i = curY; i < (curY + curHeight); i++)
					px = max(px, xMax[i]);
	
				curX = px - curX - 1;
				if(curX < 0)
					curX = 0;
      }
      else if (curY < 0) 
			{
				if (curWidth <= 0) 
				{
					curWidth += r->m_width - curX;
					if (curWidth < 1)
						curWidth = 1;
				}
	
				py = 0;
				for (i = curX; i < (curX + curWidth); i++)
					py = max(py, yMax[i]);
	
				curY = py - curY - 1;
				if(curY < 0)
					curY = 0;
      }
      
      if (curWidth <= 0) 
			{
				curWidth += r->m_width - curX;
				if (curWidth < 1)
					curWidth = 1;
      }
      
      if (curHeight <= 0) 
			{
				curHeight += r->m_height - curY;
				if (curHeight < 1)
					curHeight = 1;
      }
      
      px = curX + curWidth;
      py = curY + curHeight;
      
      for (i = curX; i < (curX + curWidth); i++) 
			{ 
				yMax[i] = py; 
			}
      for (i = curY; i < (curY + curHeight); i++) 
			{ 
				xMax[i] = px; 
			}

      
      /* Make negative sizes start a new row/column */
      if (constraints->m_gridheight == 0 && constraints->m_gridwidth == 0)
				curRow = curCol = -1;
      if (constraints->m_gridheight == 0 && curRow < 0)
				curCol = curX + curWidth;
      else if (constraints->m_gridwidth == 0 && curCol < 0)
        curRow = curY + curHeight;

      
      /* Assign the new values to the gridbag slave */
      constraints->m_tempX = curX;
      constraints->m_tempY = curY;
      constraints->m_tempWidth = curWidth;
      constraints->m_tempHeight = curHeight;

    }
    
    /*
     * Apply minimum row/column dimensions and weights
     */
    if (m_columnWidths != NULL)
      ::memcpy(&r->m_minWidth[0], m_columnWidths, m_columnWidthsLength * sizeof(int));
    if (m_rowHeights != NULL)
      ::memcpy(&r->m_minHeight[0], m_columnWidths, m_rowHeightsLength * sizeof(int));
    if (m_columnWeights != NULL)
      ::memcpy(&r->m_weightX[0], m_columnWeights, m_columnWeightsLength * sizeof(double));
    if (m_rowWeights != NULL)
      ::memcpy(&r->m_weightY[0], m_rowWeights, m_rowWeightsLength * sizeof(double));

    /*
     * Pass #3
     *
     * Distribute the minimun widths and weights:
     */
    
    nextSize = 0x7fffffff;
    
    for (i = 1; i != 0x7fffffff; i = nextSize, nextSize = 0x7fffffff) 
		{
      for (compindex = 0 ; compindex < components.GetSize() ; compindex++) 
			{
				comp = (AWTComponent*)components[compindex];
				if (!comp->isVisible())
				  continue;
				constraints = lookupConstraints(comp);
      
				if (constraints->m_tempWidth == i) 
				{
					px = constraints->m_tempX + constraints->m_tempWidth; /* right column */
	  
				  weight_diff = constraints->m_weightx;
					for (k = constraints->m_tempX; k < px; k++)
					weight_diff -= r->m_weightX[k];
					if (weight_diff > 0.0) 
					{
						weight = 0.0;
						for (k = constraints->m_tempX; k < px; k++)
							weight += r->m_weightX[k];
						for (k = constraints->m_tempX; weight > 0.0 && k < px; k++) 
						{
							double wt = r->m_weightX[k];
							double dx = (wt * weight_diff) / weight;
							r->m_weightX[k] += dx;
							weight_diff -= dx;
							weight -= wt;
						}
						/* Assign the remainder to the rightmost cell */
						r->m_weightX[px-1] += weight_diff;
					}
	  
					pixels_diff =  constraints->m_minWidth + constraints->m_ipadx +
						constraints->getInsets()->left + constraints->getInsets()->right;

					for (k = constraints->m_tempX; k < px; k++)
						pixels_diff -= r->m_minWidth[k];
					if (pixels_diff > 0) 
					{
						weight = 0.0;
						for (k = constraints->m_tempX; k < px; k++)
							weight += r->m_weightX[k];
						for (k = constraints->m_tempX; weight > 0.0 && k < px; k++) 
						{
							double wt = r->m_weightX[k];
							int dx = (int)((wt * ((double)pixels_diff)) / weight);
							r->m_minWidth[k] += dx;
							pixels_diff -= dx;
							weight -= wt;
						}
						/* Any leftovers go into the rightmost cell */
						r->m_minWidth[px-1] += pixels_diff;
					}
				}
				else if (constraints->m_tempWidth > i && constraints->m_tempWidth < nextSize)
				{
				  nextSize = constraints->m_tempWidth;
				}
	
				if (constraints->m_tempHeight == i) 
				{
					py = constraints->m_tempY + constraints->m_tempHeight; /* bottom row */
			
					weight_diff = constraints->m_weighty;
					for (k = constraints->m_tempY; k < py; k++)
						weight_diff -= r->m_weightY[k];
					if (weight_diff > 0.0) 
					{
						weight = 0.0;
						for (k = constraints->m_tempY; k < py; k++)
							weight += r->m_weightY[k];
						for (k = constraints->m_tempY; weight > 0.0 && k < py; k++) 
						{
							double wt = r->m_weightY[k];
							double dy = (wt * weight_diff) / weight;
							r->m_weightY[k] += dy;
							weight_diff -= dy;
							weight -= wt;
						}
						/* Assign the remainder to the bottom cell */
						r->m_weightY[py-1] += weight_diff;
					}
			
				/*
				 * Calculate the minHeight array values.
				 * First, figure out how tall the current slave needs to be.
				 * Then, see if it will fit within the current minHeight values.
				 * If it will not fit, add the difference according to the
				 * weightY array.
				 */
				
				pixels_diff = constraints->m_minHeight + constraints->m_ipady +
					constraints->getInsets()->top + constraints->getInsets()->bottom;
				for (k = constraints->m_tempY; k < py; k++)
					pixels_diff -= r->m_minHeight[k];
				if (pixels_diff > 0) 
				{
					weight = 0.0;
					for (k = constraints->m_tempY; k < py; k++)
						weight += r->m_weightY[k];
					for (k = constraints->m_tempY; weight > 0.0 && k < py; k++) 
					{
						double wt = r->m_weightY[k];
						int dy = (int)((wt * ((double)pixels_diff)) / weight);
						r->m_minHeight[k] += dy;
						pixels_diff -= dy;
						weight -= wt;
					}
					/* Any leftovers go into the bottom cell */
					r->m_minHeight[py-1] += pixels_diff;
				}
			}
			else if (constraints->m_tempHeight > i && constraints->m_tempHeight < nextSize)
				nextSize = constraints->m_tempHeight;
		}
	}

	delete xMax;
	delete yMax;

  return r;
}

void AWTGridBagLayout::AdjustForGravity(AWTGridBagConstraints* constraints, CRect* r) 
{
    int diffx, diffy;
		int width, height;

    width = r->Width();
    r->left += constraints->getInsets()->left;
		width -= (constraints->getInsets()->left + constraints->getInsets()->right);
    r->right = r->left + width;
    height = r->Height();
		r->top += constraints->getInsets()->top;
		height -= (constraints->getInsets()->top + constraints->getInsets()->bottom);
		r->bottom = r->top + height;

    diffx = 0;
    if ((constraints->m_fill != AWTGridBagConstraints::HORIZONTAL &&
			constraints->m_fill != AWTGridBagConstraints::BOTH)
			&& (r->Width() > (constraints->m_minWidth + constraints->m_ipadx))) 
		{
			diffx = r->Width() - (constraints->m_minWidth + constraints->m_ipadx);
			width = constraints->m_minWidth + constraints->m_ipadx;
	    r->right = r->left + width;
		}
    
		diffy = 0;
		if ((constraints->m_fill != AWTGridBagConstraints::VERTICAL &&
			constraints->m_fill != AWTGridBagConstraints::BOTH)
			&& (r->Height() > (constraints->m_minHeight + constraints->m_ipady))) 
		{
			diffy = r->Height() - (constraints->m_minHeight + constraints->m_ipady);
			height = constraints->m_minHeight + constraints->m_ipady;
			r->bottom = r->top + height;
		}

		width = r->Width();
		height = r->Height();
		    
    switch (constraints->m_anchor) 
		{
    case AWTGridBagConstraints::CENTER:
      r->left += diffx/2;
      r->top += diffy/2;
      break;
    case AWTGridBagConstraints::NORTH:
      r->left += diffx/2;
      break;
    case AWTGridBagConstraints::NORTHEAST:
      r->left += diffx;
      break;
    case AWTGridBagConstraints::EAST:
      r->left += diffx;
      r->top += diffy/2;
      break;
    case AWTGridBagConstraints::SOUTHEAST:
      r->left += diffx;
      r->top += diffy;
      break;
    case AWTGridBagConstraints::SOUTH:
      r->left += diffx/2;
      r->top += diffy;
      break;
    case AWTGridBagConstraints::SOUTHWEST:
      r->top += diffy;
      break;
    case AWTGridBagConstraints::WEST:
      r->top += diffy/2;
      break;
    case AWTGridBagConstraints::NORTHWEST:
      break;
    }

		r->right = r->left + width;
		r->bottom = r->top + height;
}

CSize AWTGridBagLayout::GetMinSize(AWTContainer* parent, AWTGridBagLayoutInfo* info) 
{
    CSize d(0,0);
    int i, t;
    AWTInsets insets = parent->getInsets();

    t = 0;
    for(i = 0; i < info->m_width; i++)
      t += info->m_minWidth[i];
    d.cx = t + insets.left + insets.right;

    t = 0;
    for(i = 0; i < info->m_height; i++)
      t += info->m_minHeight[i];
    d.cy = t + insets.top + insets.bottom;

    return d;
}

void AWTGridBagLayout::ArrangeGrid(AWTContainer* parent) 
{
    AWTComponent* comp;
    int compindex;
    AWTGridBagConstraints* constraints;
    AWTInsets insets = parent->getInsets();
		CObArray components;
    CSize d;
    CRect r(0,0,0,0);
    int i, diffw, diffh;
    double weight;
    AWTGridBagLayoutInfo* info;
		int width, height;
    
    /*
     * If the parent has no slaves anymore, then don't do anything
     * at all:  just leave the parent's size as-is.
     */
    parent->getComponents(&components);
    if (components.GetSize() == 0 &&
			(m_columnWidths == NULL || m_columnWidthsLength == 0) &&
			(m_rowHeights == NULL || m_rowHeightsLength == 0)) 
		{
      return;
    }
    
    /*
     * Pass #1: scan all the slaves to figure out the total amount
     * of space needed.
     */
    
    info = GetLayoutInfo(parent, PREFERREDSIZE);
    d = GetMinSize(parent, info);

    if (parent->getWidth() < d.cx || parent->getHeight() < d.cy) 
		{
			delete info;
		  info = GetLayoutInfo(parent, MINSIZE);
      d = GetMinSize(parent, info);
    }

    if (m_layoutInfo != NULL)
			delete m_layoutInfo;
		m_layoutInfo = info;
    r.right = d.cx;
    r.bottom = d.cy;

    
    diffw = parent->getWidth() - r.Width();
    if (diffw != 0) 
		{
      weight = 0.0;
      for (i = 0; i < info->m_width; i++)
				weight += info->m_weightX[i];
      if (weight > 0.0) 
			{
				for (i = 0; i < info->m_width; i++) 
				{
					int dx = (int)(( ((double)diffw) * info->m_weightX[i]) / weight);
					info->m_minWidth[i] += dx;
					r.right += dx;
					if (info->m_minWidth[i] < 0) 
					{
						r.right -= info->m_minWidth[i];
						info->m_minWidth[i] = 0;
					}
				}
      }
      diffw = parent->getWidth() - r.right;
    }
    else 
		{
      diffw = 0;
    }
    
    diffh = parent->getHeight() - r.Height();
    if (diffh != 0) 
		{
      weight = 0.0;
      for (i = 0; i < info->m_height; i++)
				weight += info->m_weightY[i];
			if (weight > 0.0) 
			{
				for (i = 0; i < info->m_height; i++) 
				{
					int dy = (int)(( ((double)diffh) * info->m_weightY[i]) / weight);
					info->m_minHeight[i] += dy;
					r.bottom += dy;
					if (info->m_minHeight[i] < 0) 
					{
						r.bottom -= info->m_minHeight[i];
						info->m_minHeight[i] = 0;
					}
				}
      }
      diffh = parent->getHeight() - r.Height();
    }
    else 
		{
      diffh = 0;
    }

    /*
     * Now do the actual layout of the slaves using the layout information
     * that has been collected.
     */
    
    info->m_startx = diffw/2 + insets.left;
    info->m_starty = diffh/2 + insets.top;

    for (compindex = 0 ; compindex < components.GetSize() ; compindex++) 
		{
      comp = (AWTComponent*)components[compindex];
      if (!comp->isVisible())
				continue;
      constraints = lookupConstraints(comp);

      r.left = info->m_startx;
      for(i = 0; i < constraints->m_tempX; i++)
				r.left += info->m_minWidth[i];
      
      r.top = info->m_starty;
      for(i = 0; i < constraints->m_tempY; i++)
				r.top += info->m_minHeight[i];
      
      width = 0;
      for(i = constraints->m_tempX; i < (constraints->m_tempX + constraints->m_tempWidth); i++) 
			{
				width += info->m_minWidth[i];
      }
			r.right = r.left + width;
      
      height = 0;
      for(i = constraints->m_tempY; i < (constraints->m_tempY + constraints->m_tempHeight); i++) 
			{
				height += info->m_minHeight[i];
      }
			r.bottom = r.top + height;
      
      AdjustForGravity(constraints, &r);
      
      /*
       * If the window is too small to be interesting then
       * unmap it.  Otherwise configure it and then make sure
       * it's mapped.
       */
      
      if ((r.Width() <= 0) || (r.Height() <= 0)) 
			{
				comp->setBounds(0, 0, 0, 0);
      }
      else 
			{
				CPoint pt = comp->getLocation();
				CSize sz = comp->getSize();
				if (pt.x != r.left || pt.y != r.top || sz.cx != r.Width() || sz.cy != r.Height()) 
				{
					comp->setBounds(r.left, r.top, r.Width(), r.Height());
				}
      }
    }
}

AWTGridBagLayoutInfo::AWTGridBagLayoutInfo()
{
	m_width = 0;
	m_height = 0;
	m_startx = 0;
	m_starty = 0;
	for (int n = 0; n < MAXGRIDSIZE; n++)
	{
		m_minWidth[n] = 0;	
		m_minHeight[n] = 0;	
		m_weightX[n] = 0.0;	
		m_weightY[n] = 0.0;	
	}
}

AWTGridBagLayoutInfo::~AWTGridBagLayoutInfo()
{
}

/*
void log(LPTSTR sz)
{
	FILE* out = fopen("log.txt", "a");
	fputs(sz, out);
	fclose(out);
}
*/
